﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text.RegularExpressions;
using CardPlay.Model.Primitives;

namespace CardPlay.Model.CardModel
{
    public class CardFace : IEquatable<CardFace>
    {
        private readonly Rank _rank = Rank.Ace;
        private readonly Suit _suit = Suit.Clubs;

        public static readonly int SuitsPerDeck = 4;
        public static readonly int RanksPerSuit = 13;
        public static readonly int CardsPerStandardDeck = SuitsPerDeck * RanksPerSuit;

        public static readonly Suit[] AllSuits = Enum<Suit>.Values.ToArray();
        
        public CardFace(Rank rank, Suit suit)
        {
            Contract.Requires<ArgumentOutOfRangeException>(rank >= Rank.Ace && rank <= Rank.King);
            Contract.Requires<ArgumentOutOfRangeException>(Enum<Suit>.Values.Contains(suit));

            _rank = rank;
            _suit = suit;
        }

        public Rank Rank
        {
            get { return _rank; }
        }

        public Suit Suit
        {
            get { return _suit; }
        }

        public bool Equals(CardFace other) 
        {
            return !ReferenceEquals(other, null) && (ReferenceEquals(other, this) || (Rank == other.Rank && Suit == other.Suit));
        }

        public override bool Equals(Object obj)
        {
            return Equals(obj as CardFace);
        }

        public override string ToString()
        {
            return String.Format("{0} of {1}", Rank, Suit);
        }

        public override int GetHashCode()
        {
            return (int)Rank * RanksPerSuit + (int)Suit - 1;
        }

        public static bool operator ==(CardFace face1, CardFace face2)
        {
            return !ReferenceEquals(face1, null) && face1.Equals(face2);
        }

        public static bool operator !=(CardFace face1, CardFace face2)
        {
            return !(face1 == face2);
        }

        public static CardFace Parse(string expression)
        {
            try
            {
                var matches = Regex.Matches(expression, @"(?<rank>\w+)? of (?<suit>(hearts|spades|diamonds|clubs))?", RegexOptions.IgnoreCase);
                if (matches.Count == 1)
                {
                    var rank = Enum<Rank>.Parse(matches[0].Groups["rank"].Value);
                    var suit = Enum<Suit>.Parse(matches[0].Groups["suit"].Value);

                    return new CardFace(rank, suit);
                }
            }
// ReSharper disable EmptyGeneralCatchClause
            catch
// ReSharper restore EmptyGeneralCatchClause
            {
            }

            throw new FormatException("Failed to parse: " + expression);
        }

        public static bool TryParse(string expression, out CardFace result)
        {
            try
            {
                result = Parse(expression);
                return true;
            }
            catch (Exception)
            {
                result = null;
            }

            return false;
        }

        public static IEnumerable<CardFace> StandardSet
        {
            get
            {
                return AllSuits.SelectMany(suit => Enum<Rank>.Values, (suit, rank) => new CardFace(rank, suit));
            }
        }
    }
}
